"""
音频流会话管理服务
"""

import asyncio
import uuid
from typing import Dict, List, Any, Optional, Set
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
import json

from ...core.exceptions import BusinessLogicError, ValidationError, NotFoundError
from ...core.logging import get_logger, audit_logger
from ...core.config import settings
from ...models.audio_stream import (
    AudioStream, StreamStatus, StreamType, 
    StreamSession, StreamConnectionLog
)
# Note: audio_stream repository doesn't exist, using base repository
from ...repositories.base import BaseRepository
from ...services.cache import cache_service, CacheContext

logger = get_logger(__name__)


class SessionState(Enum):
    """会话状态"""
    INITIALIZING = "initializing"
    ACTIVE = "active"
    PAUSED = "paused"
    BUFFERING = "buffering"
    ENDED = "ended"
    ERROR = "error"


@dataclass
class StreamSession:
    """流会话"""
    id: str
    user_id: str
    audiobook_id: Optional[str] = None
    stream_type: StreamType = StreamType.FILE_STREAM
    session_type: str = "single"  # single, playlist, radio
    is_active: bool = True
    auto_play: bool = True
    repeat_mode: str = "none"  # none, one, all
    playback_rate: float = 1.0
    volume: float = 1.0
    
    # 会话数据
    current_index: int = 0
    current_position: float = 0.0
    buffer_position: float = 0.0
    
    # 统计信息
    total_playtime: float = 0.0
    streams_played: int = 0
    
    # 时间戳
    created_at: datetime = field(default_factory=datetime.utcnow)
    updated_at: datetime = field(default_factory=datetime.utcnow)
    last_activity_at: datetime = field(default_factory=datetime.utcnow)
    
    # 连接信息
    connected_clients: Set[str] = field(default_factory=set)
    websocket_connections: Dict[str, Dict[str, Any]] = field(default_factory=dict)
    
    def __post_init__(self):
        if isinstance(self.connected_clients, list):
            self.connected_clients = set(self.connected_clients)


class SessionManager:
    """会话管理器"""
    
    def __init__(self):
        # Note: Using BaseRepository as placeholder since specific repositories don't exist
        self.session_repo = None
        self.connection_repo = None
        self.active_sessions: Dict[str, StreamSession] = {}  # session_id -> session
        self.user_sessions: Dict[str, List[str]] = {}  # user_id -> [session_ids]
        
        # 配置
        self.max_sessions_per_user = 10
        self.session_timeout = 3600  # 1小时
        self.cleanup_interval = 300  # 5分钟
        
        # 清理任务（延迟启动）
        self._cleanup_task = None
    
    def start_cleanup_task(self):
        """启动清理任务"""
        if self._cleanup_task is None:
            try:
                loop = asyncio.get_running_loop()
                self._cleanup_task = loop.create_task(self._cleanup_expired_sessions())
            except RuntimeError:
                # 没有运行中的事件循环，延迟启动
                logger.debug("No running event loop, cleanup task will start on first use")
    
    def stop_cleanup_task(self):
        """停止清理任务"""
        if self._cleanup_task:
            self._cleanup_task.cancel()
            self._cleanup_task = None
    
    async def create_session(
        self,
        user_id: str,
        session_type: str = "single",
        stream_type: StreamType = StreamType.FILE_STREAM,
        audiobook_id: Optional[str] = None,
        **kwargs
    ) -> StreamSession:
        """创建流会话"""
        try:
            # 检查用户会话数量限制
            await self._check_user_session_limit(user_id)
            
            # 创建会话
            session_id = str(uuid.uuid4())
            session = StreamSession(
                id=session_id,
                user_id=user_id,
                session_type=session_type,
                stream_type=stream_type,
                audiobook_id=audiobook_id,
                **kwargs
            )
            
            # 保存到内存
            self.active_sessions[session_id] = session
            
            # 更新用户会话列表
            if user_id not in self.user_sessions:
                self.user_sessions[user_id] = []
            self.user_sessions[user_id].append(session_id)
            
            # 保存到数据库
            session_data = {
                "id": session_id,
                "user_id": user_id,
                "session_type": session_type,
                "client_info": kwargs.get("client_info", {})
            }
            
            # Note: Repository not available, skip database save
            # db_session = await self.session_repo.create_with_attributes(**session_data)
            
            # 记录审计日志
            audit_logger.log_user_action(
                user_id=user_id,
                action="create_stream_session",
                resource="stream_session",
                details={
                    "session_id": session_id,
                    "session_type": session_type,
                    "stream_type": stream_type.value
                }
            )
            
            logger.info(f"Created stream session {session_id} for user {user_id}")
            return session
            
        except Exception as e:
            logger.error(f"Failed to create session: {e}")
            raise
    
    async def get_session(self, session_id: str, user_id: Optional[str] = None) -> Optional[StreamSession]:
        """获取流会话"""
        # 先从内存获取
        session = self.active_sessions.get(session_id)
        
        if session:
            # 检查权限
            if user_id and session.user_id != user_id:
                return None
            
            # 更新活动时间
            session.last_activity_at = datetime.utcnow()
            return session
        
            # 从数据库获取
        try:
            if self.session_repo:
                db_session = await self.session_repo.get(session_id)
            else:
                db_session = None
            if not db_session:
                return None
            
            # 检查权限
            if user_id and db_session.user_id != user_id:
                return None
            
            # 重建内存会话
            session = StreamSession(
                id=db_session.id,
                user_id=db_session.user_id,
                session_type=db_session.session_type,
                client_info=db_session.client_info,
                total_playtime=db_session.total_playtime,
                streams_played=db_session.streams_played,
                created_at=db_session.created_at,
                last_activity_at=db_session.last_activity_at
            )
            
            # 添加到内存
            self.active_sessions[session_id] = session
            
            # 更新用户会话列表
            if db_session.user_id not in self.user_sessions:
                self.user_sessions[db_session.user_id] = []
            if session_id not in self.user_sessions[db_session.user_id]:
                self.user_sessions[db_session.user_id].append(session_id)
            
            return session
            
        except Exception as e:
            logger.error(f"Failed to get session from database: {e}")
            return None
    
    async def update_session(
        self,
        session_id: str,
        user_id: str,
        **kwargs
    ) -> bool:
        """更新流会话"""
        try:
            session = await self.get_session(session_id, user_id)
            if not session:
                return False
            
            # 更新内存会话
            for key, value in kwargs.items():
                if hasattr(session, key):
                    setattr(session, key, value)
            
            session.updated_at = datetime.utcnow()
            session.last_activity_at = datetime.utcnow()
            
            # 更新数据库
            update_data = {k: v for k, v in kwargs.items() if k in [
                "current_index", "current_position", "buffer_position",
                "total_playtime", "streams_played", "last_activity_at"
            ]}
            
            if update_data and self.session_repo:
                await self.session_repo.update_by_id(session_id, update_data)
            
            return True
            
        except Exception as e:
            logger.error(f"Failed to update session: {e}")
            return False
    
    async def delete_session(self, session_id: str, user_id: str) -> bool:
        """删除流会话"""
        try:
            session = await self.get_session(session_id, user_id)
            if not session:
                return False
            
            # 断开所有连接
            for connection_id in list(session.websocket_connections.keys()):
                await self.disconnect_client(session_id, connection_id)
            
            # 从内存移除
            if session_id in self.active_sessions:
                del self.active_sessions[session_id]
            
            # 从用户会话列表移除
            if user_id in self.user_sessions:
                if session_id in self.user_sessions[user_id]:
                    self.user_sessions[user_id].remove(session_id)
                
                # 清理空列表
                if not self.user_sessions[user_id]:
                    del self.user_sessions[user_id]
            
            # 从数据库删除
            if self.session_repo:
                await self.session_repo.remove(session_id)
            
            # 记录审计日志
            audit_logger.log_user_action(
                user_id=user_id,
                action="delete_stream_session",
                resource="stream_session",
                details={
                    "session_id": session_id,
                    "duration": datetime.utcnow() - session.created_at
                }
            )
            
            logger.info(f"Deleted stream session {session_id}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to delete session: {e}")
            return False
    
    async def connect_client(
        self,
        session_id: str,
        user_id: str,
        connection_id: str,
        client_info: Dict[str, Any]
    ) -> bool:
        """客户端连接"""
        try:
            session = await self.get_session(session_id, user_id)
            if not session:
                return False
            
            # 添加连接
            session.connected_clients.add(connection_id)
            session.websocket_connections[connection_id] = {
                "connected_at": datetime.utcnow(),
                "client_info": client_info,
                "last_ping": datetime.utcnow(),
                "bytes_sent": 0,
                "messages_sent": 0
            }
            
            session.last_activity_at = datetime.utcnow()
            
            # 记录连接日志
            connection_log = {
                "stream_id": session_id,
                "connection_type": "websocket",
                "event_type": "connect",
                "connection_id": connection_id,
                "client_info": client_info,
                "ip_address": client_info.get("ip_address"),
                "user_agent": client_info.get("user_agent")
            }
            
            # Note: Repository not available, skip connection log
            # await self.connection_repo.create_with_attributes(**connection_log)
            
            logger.info(f"Client {connection_id} connected to session {session_id}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to connect client: {e}")
            return False
    
    async def disconnect_client(
        self,
        session_id: str,
        connection_id: str,
        user_id: Optional[str] = None
    ) -> bool:
        """客户端断开连接"""
        try:
            session = self.active_sessions.get(session_id)
            if not session:
                return False
            
            # 权限检查
            if user_id and session.user_id != user_id:
                return False
            
            # 检查连接是否存在
            if connection_id not in session.websocket_connections:
                return False
            
            # 获取连接信息
            connection_info = session.websocket_connections[connection_id]
            
            # 移除连接
            session.connected_clients.discard(connection_id)
            del session.websocket_connections[connection_id]
            
            # 记录断开连接日志
            connection_log = {
                "stream_id": session_id,
                "connection_type": "websocket",
                "event_type": "disconnect",
                "connection_id": connection_id,
                "connection_time": (datetime.utcnow() - connection_info["connected_at"]).total_seconds(),
                "bytes_transferred": connection_info["bytes_sent"]
            }
            
            # Note: Repository not available, skip connection log
            # await self.connection_repo.create_with_attributes(**connection_log)
            
            logger.info(f"Client {connection_id} disconnected from session {session_id}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to disconnect client: {e}")
            return False
    
    async def get_user_sessions(
        self,
        user_id: str,
        include_inactive: bool = False
    ) -> List[StreamSession]:
        """获取用户的所有会话"""
        try:
            # 从内存获取活跃会话
            active_sessions = []
            if user_id in self.user_sessions:
                for session_id in self.user_sessions[user_id]:
                    if session_id in self.active_sessions:
                        session = self.active_sessions[session_id]
                        if include_inactive or session.is_active:
                            active_sessions.append(session)
            
            if include_inactive and self.session_repo:
                # 从数据库获取所有会话
                db_sessions = await self.session_repo.get_by_user(user_id)
                
                # 合并去重
                db_session_ids = {s.id for s in db_sessions}
                active_session_ids = {s.id for s in active_sessions}
                
                inactive_sessions = [
                    self._db_session_to_stream_session(s)
                    for s in db_sessions
                    if s.id not in active_session_ids
                ]
                
                return active_sessions + inactive_sessions
            
            return active_sessions
            
        except Exception as e:
            logger.error(f"Failed to get user sessions: {e}")
            return []
    
    async def get_session_statistics(self, session_id: str, user_id: str) -> Optional[Dict[str, Any]]:
        """获取会话统计信息"""
        try:
            session = await self.get_session(session_id, user_id)
            if not session:
                return None
            
            # 计算连接统计
            total_connections = len(session.websocket_connections)
            active_connections = len(session.connected_clients)
            
            # 计算传输统计
            total_bytes_sent = sum(
                conn["bytes_sent"] for conn in session.websocket_connections.values()
            )
            
            # 获取连接日志统计
            connection_logs = []
            if self.connection_repo:
                connection_logs = await self.connection_repo.get_connection_logs(session_id)
            
            stats = {
                "session_id": session_id,
                "user_id": session.user_id,
                "session_type": session.session_type,
                "is_active": session.is_active,
                "total_playtime": session.total_playtime,
                "streams_played": session.streams_played,
                "current_index": session.current_index,
                "current_position": session.current_position,
                "total_connections": total_connections,
                "active_connections": active_connections,
                "total_bytes_sent": total_bytes_sent,
                "created_at": session.created_at.isoformat(),
                "last_activity_at": session.last_activity_at.isoformat(),
                "duration": (datetime.utcnow() - session.created_at).total_seconds(),
                "connection_count": len(connection_logs),
                "avg_connection_duration": self._calculate_avg_connection_duration(connection_logs)
            }
            
            return stats
            
        except Exception as e:
            logger.error(f"Failed to get session statistics: {e}")
            return None
    
    async def update_session_activity(
        self,
        session_id: str,
        activity_type: str,
        **kwargs
    ) -> bool:
        """更新会话活动"""
        try:
            session = self.active_sessions.get(session_id)
            if not session:
                return False
            
            session.last_activity_at = datetime.utcnow()
            
            # 更新播放统计
            if activity_type == "play":
                session.total_playtime += kwargs.get("duration", 0)
            elif activity_type == "stream_complete":
                session.streams_played += 1
            
            return True
            
        except Exception as e:
            logger.error(f"Failed to update session activity: {e}")
            return False
    
    async def cleanup_expired_sessions(self) -> int:
        """清理过期会话"""
        try:
            expired_count = 0
            current_time = datetime.utcnow()
            
            # 检查内存中的会话
            expired_sessions = []
            
            for session_id, session in self.active_sessions.items():
                # 检查会话是否过期
                if current_time - session.last_activity_at > timedelta(seconds=self.session_timeout):
                    # 如果没有活跃连接，标记为过期
                    if not session.connected_clients:
                        expired_sessions.append(session_id)
            
            # 清理过期会话
            for session_id in expired_sessions:
                session = self.active_sessions[session_id]
                
                # 断开所有连接
                for connection_id in list(session.websocket_connections.keys()):
                    await self.disconnect_client(session_id, connection_id)
                
                # 从内存移除
                del self.active_sessions[session_id]
                
                # 更新用户会话列表
                if session.user_id in self.user_sessions:
                    if session_id in self.user_sessions[session.user_id]:
                        self.user_sessions[session.user_id].remove(session_id)
                    
                    if not self.user_sessions[session.user_id]:
                        del self.user_sessions[session.user_id]
                
                expired_count += 1
                logger.info(f"Cleaned up expired session {session_id}")
            
            # 清理数据库中的过期会话
            db_expired_count = 0
            if self.session_repo:
                cutoff_time = current_time - timedelta(seconds=self.session_timeout * 2)
                db_expired_count = await self.session_repo.cleanup_expired_sessions(cutoff_time)
            
            total_cleaned = expired_count + db_expired_count
            
            if total_cleaned > 0:
                logger.info(f"Cleaned up {total_cleaned} expired sessions")
            
            return total_cleaned
            
        except Exception as e:
            logger.error(f"Failed to cleanup expired sessions: {e}")
            return 0
    
    async def _cleanup_expired_sessions(self):
        """定期清理过期会话的后台任务"""
        while True:
            try:
                await asyncio.sleep(self.cleanup_interval)
                await self.cleanup_expired_sessions()
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Session cleanup task error: {e}")
                await asyncio.sleep(60)  # 出错时等待1分钟再重试
    
    async def _check_user_session_limit(self, user_id: str):
        """检查用户会话数量限制"""
        try:
            if user_id in self.user_sessions:
                active_count = len([
                    sid for sid in self.user_sessions[user_id]
                    if sid in self.active_sessions and self.active_sessions[sid].is_active
                ])
                
                if active_count >= self.max_sessions_per_user:
                    # 清理最旧的会话
                    await self._cleanup_oldest_session(user_id)
            
        except Exception as e:
            logger.error(f"Failed to check user session limit: {e}")
    
    async def _cleanup_oldest_session(self, user_id: str):
        """清理用户最旧的会话"""
        try:
            if user_id not in self.user_sessions:
                return
            
            user_sessions = []
            for session_id in self.user_sessions[user_id]:
                if session_id in self.active_sessions:
                    session = self.active_sessions[session_id]
                    user_sessions.append((session_id, session.last_activity_at))
            
            if len(user_sessions) < 2:
                return
            
            # 按最后活动时间排序，移除最旧的
            user_sessions.sort(key=lambda x: x[1])
            oldest_session_id = user_sessions[0][0]
            
            await self.delete_session(oldest_session_id, user_id)
            logger.info(f"Cleaned up oldest session {oldest_session_id} for user {user_id}")
            
        except Exception as e:
            logger.error(f"Failed to cleanup oldest session: {e}")
    
    def _db_session_to_stream_session(self, db_session) -> StreamSession:
        """将数据库会话转换为流会话对象"""
        return StreamSession(
            id=db_session.id,
            user_id=db_session.user_id,
            session_type=db_session.session_type,
            client_info=db_session.client_info,
            total_playtime=db_session.total_playtime,
            streams_played=db_session.streams_played,
            created_at=db_session.created_at,
            last_activity_at=db_session.last_activity_at,
            is_active=False  # 数据库中的会话默认为非活跃
        )
    
    def _calculate_avg_connection_duration(self, connection_logs) -> float:
        """计算平均连接时长"""
        try:
            if not connection_logs:
                return 0.0
            
            completed_connections = [
                log for log in connection_logs
                if log.event_type == "disconnect" and hasattr(log, 'connection_time')
            ]
            
            if not completed_connections:
                return 0.0
            
            total_duration = sum(log.connection_time for log in completed_connections)
            return total_duration / len(completed_connections)
            
        except Exception as e:
            logger.error(f"Failed to calculate avg connection duration: {e}")
            return 0.0
    
    def get_active_sessions_count(self) -> int:
        """获取活跃会话数量"""
        return len(self.active_sessions)
    
    def get_total_connections_count(self) -> int:
        """获取总连接数量"""
        total = 0
        for session in self.active_sessions.values():
            total += len(session.connected_clients)
        return total


# 全局会话管理器实例
session_manager = SessionManager()
